package org.dormi.learn;

import lombok.AllArgsConstructor;
import lombok.Data;

import java.util.Arrays;
import java.util.List;

/**
 * @author : dormi330
 * @since : 21/12/01
 * 问题：为什么需要 combiner。 reduce(U identity, accumulator, combiner)
 * 这里有答案 https://stackoverflow.com/questions/24308146/why-is-a-combiner-needed-for-reduce-method-that-converts-type-in-java-8
 * 简单来说：
 * 1. 并行和串行api设计应该无区别
 * 2. combiner在并行的时候才会使用，串行的时候不会使用
 * 并行时，combiner将 accumulator进行combine
 *
 * 2021-12-14 在 MapReduce框架中，也有个 Combiner组件，这个组件是map时进行reduce的操作
 */
public class StreamReduceAPI {

    @Data
    @AllArgsConstructor
    private static class User {
        private String sex;
        private Integer age;
    }

    public static void main(String[] args) {
        Integer ageSummed1 = prepareData().stream()
            .filter(person -> person.getSex().equals("m"))
            /* <U> U reduce(U identity, BiFunction<U, ? super T, U> accumulator, BinaryOperator<U> combiner); */
            .reduce(0, (Integer sum, User p) -> sum + p.getAge(), Integer::sum);


        // 第二种方法
        Integer ageSummed2 = prepareData().stream()
            .filter(u -> u.getSex().equals("m"))
            .map(u -> u.getAge())
            // T reduce(T identity, BinaryOperator<T> accumulator);
            .reduce(0, (sum, age) -> sum + age);

        // 第二种方法优化
        Integer ageSummed3 = prepareData().stream()
            .filter(u -> u.getSex().equals("m"))
            .map(User::getAge)
            .reduce(0, Integer::sum);

        // stream空的时候
        Integer ageSummed4 = prepareData().stream()
            .filter(u -> u.getSex().equals("xxx"))
            .map(User::getAge)
            .reduce(0, Integer::sum);

        System.out.println(ageSummed1);
        System.out.println(ageSummed2);
        System.out.println(ageSummed3);
        System.out.println(ageSummed4);
    }

    private static List<User> prepareData() {
        return Arrays.asList(
            new User("m", 10),
            new User("m", 20),
            new User("f", 30),
            new User("f", 40)
        );
    }
}
